home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 June: Reference Library / Dev.CD Jun 94.toast / Periodicals / develop / develop Issue 18 / develop 18 code / Apple Guide Sample / Source / UAppMo.cp < prev    next >
Encoding:
Text File  |  1994-04-01  |  29.4 KB  |  1,080 lines  |  [TEXT/MPS ]

  1. // Copyright ©1994 Apple Computer, Inc.
  2. // Author: John Powers
  3. // Date:   13-Mar-94
  4.  
  5. // UAppMo.cp
  6. // The derived application class for MoGuide.
  7.  
  8. #ifndef __UAPPMO__
  9.     #include "UAppMo.h"
  10. #endif
  11.  
  12. #include <Dialogs.h>
  13.  
  14.         // Segment
  15.  
  16. #pragma segment Main
  17.  
  18. // ------------------------------------------------------------------------
  19. // TAppMo::HandleAECustom
  20. // Handles the custom events.
  21. // The refCon contains our application object.
  22. // Should be in a locked, unpurgeable segment.
  23. //
  24. // Unfortunately, the DTS sample TApplication does not process
  25. // high level events.  We fix that by overriding the EventLoop
  26. // and adding high-level event processing.
  27. //
  28. pascal OSErr
  29. TAppMo::HandleAECustom(AppleEvent& theAppleEvent,
  30.                         AppleEvent& /*theReply*/, long refCon)
  31. {
  32.     OSType        eventId;
  33.     Size        actualSize;
  34.     DescType    returnedType;
  35.     OSErr        err=noErr;
  36.             // Useless without our application object.
  37.     TAppMo* ourApp=(TAppMo*)refCon;
  38.     if(!ourApp)
  39.         return kErrNoAppObject;
  40.             // Get a potential document collaboarator.
  41.     TDocArt* docArt = ourApp->GetDocArt();
  42.             // Get event id.
  43.     err = AEGetAttributePtr(&theAppleEvent, keyEventIDAttr,
  44.                                 typeType, &returnedType,
  45.                                 (Ptr) &eventId, sizeof(eventId),
  46.                                 &actualSize);
  47.     switch (eventId)
  48.     {
  49.         case kAEIdCloseDocArt:
  50.             ourApp->CloseDoc(ourApp->fDocArt);
  51.             break;
  52.         case kAEIdShowDocArt:
  53.             ourApp->ShowArt();
  54.             ourApp->DoAdvance();
  55.             break;
  56.         case kAEIdCloseDocFB:
  57.             ourApp->CloseDoc(ourApp->fDocFB);
  58.             break;
  59.         case kAEIdShowDocFB:
  60.             ourApp->ShowFeedback();
  61.             break;
  62.         case kAEIdReset:
  63.             if(docArt)
  64.                 docArt->SetWantReset();
  65.             break;
  66.         case kAEIdShuffle:
  67.             if(docArt)
  68.                 docArt->SetWantShuffle();
  69.             break;
  70.         case kAEIdQuitApp:
  71.             ourApp->ExitLoop();
  72.             break;
  73.         case kAEIdWaitCollision:
  74.                 // The context check will set the event.
  75.                 // If the event is constant, we could set it
  76.                 // right here and skip the context check.
  77.                 // Otherwise, we need the context check to
  78.                 // send us the event that the author desires.
  79.             if(docArt)
  80.                 docArt->SetWantCollision(true);
  81.             break;
  82.         case kAEIdIgnoreCollision:
  83.                 // Ignore any collision,
  84.                 // turn off flag and clear event.
  85.             if(docArt)
  86.             {
  87.                 docArt->SetWantCollision(false);
  88.                 docArt->SetCollisionEvent(0);
  89.             }
  90.             break;
  91.         case kAEIdAdvance:
  92.                 // Advance on the next open-window event.
  93.             ourApp->SetOkayAdvance(true);
  94.             break;
  95.         default:
  96.             err = errAEEventNotHandled;
  97.             break;
  98.     }
  99.     if(ourApp->fFBEvent)
  100.         ourApp->fFBEvent->SetFlash(eventId);
  101.     return err;
  102.     
  103. }
  104.  
  105. // --------------------------------------------------------------------
  106. // TAppMo::ReplyToCoach
  107. //
  108. // This is a callback invoked by the Coach Apple event handler.
  109. // This member function and the refCon
  110. // was provided by us in the Init call.
  111. //
  112. // Handles replies for Coach queries sent to OurApp.
  113. // Called with the item name and refCon.
  114. // Return the item Rect and a result code.
  115. //
  116. // The name is a zero-terminated string (C-string).
  117. // equalstring requires an A5 world.
  118. //
  119. // Caveats:
  120. //
  121. //        Our app resource map is not available.
  122. //        If you need a resource such as a string,
  123. //        read it in advance and save it in memory.
  124. //
  125. // A static function in main segment.
  126. pascal OSErr
  127. TAppMo::ReplyToCoach(Rect* pItemRect, Ptr pName, long refCon)
  128. {
  129.     OSErr    result=kAGErrItemNotFound;
  130.             // We probably need an A5 world.
  131.     long restoreGlobals = SetA5(*(long*)CurrentA5);
  132.             // Our application object is in the refcon.
  133.     TAppMo* ourApp=(TAppMo*) refCon;
  134.     if(ourApp)
  135.     {
  136.             // Ask our document window for the rect of the named item.
  137.         if(ourApp->fDocArt->GetLocation(pName, pItemRect))
  138.         {
  139.             GrafPtr    savePort;
  140.                 // Return the rectangle in global coordinates
  141.             GetPort(&savePort);
  142.                 // Get the GrafPort.
  143.             SetPort((GrafPtr) ourApp->fDocArt->GetDocWindow());
  144.                 // Top-left
  145.             LocalToGlobal((Point*)&(pItemRect->top));
  146.                 // Bottom-right
  147.             LocalToGlobal((Point*)&(pItemRect->bottom));
  148.             SetPort(savePort);
  149.             result = noErr;
  150.         }
  151.             // Feedback - auditory and visual
  152.         if(ourApp->fFBCoach)
  153.             ourApp->fFBCoach->SetFlash(pName);
  154.     } // if(ourApp…
  155.      SetA5(restoreGlobals);
  156.     return(result);
  157. }
  158.  
  159. // ------------------------------------------------------------------------
  160. // TContext::ReplyToContext
  161. // Handles replies for context checks sent to OurApp.
  162. // Called with the data in the context check database object
  163. // and the event you sent in the Init.
  164. // Return the result (true or false).
  165. // A callback.
  166. // A static function in main segment.
  167. pascal OSErr
  168. TContext::ReplyToContext(Ptr pInput, Size inputDataSize,
  169.                             Ptr *ppOutput, Size *pOutputDataSize,
  170.                             AGAppInfoHdl hAppInfo)
  171. {
  172.     OSErr        err=noErr;
  173.     long        inputValue=0;
  174.     short        result=false;
  175.             // We probably need an A5 world.
  176.     long restoreGlobals = SetA5(*(long*)CurrentA5);
  177.             // The input value can be used by either event id.
  178.     if(pInput && inputDataSize>0)
  179.     {
  180.         inputValue = *((long*)pInput);
  181.     }
  182.             // Our application object was passed by us in the refCon.
  183.             // It is returned to us in the refcon of hAppInfo.
  184.     TAppMo* ourApp = (TAppMo*) (**hAppInfo).refCon;
  185.             // Which event id?
  186.     switch ((**hAppInfo).eventId)
  187.     {
  188.                     // kAEIdContextCollision
  189.                     // Set the collision event to the inputValue.
  190.                     // When a collision occurs, the event will
  191.                     // be sent to Apple Guide.  In other words,
  192.                     // Apple Guide is saying "Let me know when
  193.                     // a collision occurs by sending me this event."
  194.                     // A zero input value cancels the event.
  195.                     // Always returns a false result.
  196.         case kAEIdContextCollision:
  197.             if(ourApp->fDocArt)
  198.                 if(ourApp->fDocArt->WantCollision())
  199.                     ourApp->fDocArt->SetCollisionEvent(inputValue);
  200.             result = false;
  201.             break;
  202.                     // kAEIdContextBeep
  203.                     // Return the value (true or false) saved in fBeepReturn.
  204.         case kAEIdContextBeep:
  205.             result = ourApp->fBeepReturn;
  206.             break;
  207.                     // kAEIdContextWindow
  208.                     // Return true if a document window is open and showing.
  209.         case kAEIdContextWindow:
  210.             result = (ourApp->fDocArt)?ourApp->fDocArt->IsWindowVisible():false;
  211.             break;
  212.         default:
  213.                     // An unknown event will be ignored.
  214.             break;
  215.     }
  216.         // Create storage for our result.
  217.     *ppOutput = NewPtr(sizeof(short));
  218.     if(*ppOutput)
  219.     {
  220.             // Set the result and its size.
  221.         **ppOutput = result;
  222.         *pOutputDataSize = sizeof(short);
  223.     }
  224.         // Feedback - auditory and visual
  225.     if(ourApp->fFBContext)
  226.         ourApp->fFBContext->SetFlash((**hAppInfo).eventId);
  227.  
  228.      SetA5(restoreGlobals);
  229.     return err;
  230. }
  231.  
  232.         // Segment
  233.  
  234. #pragma segment MoG1
  235.  
  236. // =========================================================================
  237. // TAppMo
  238. // ------------------------------------------------------------------------
  239. TAppMo::TAppMo()
  240. {
  241. };
  242.  
  243. // ------------------------------------------------------------------------
  244. // TAppMo::AddGuideFiles
  245. // Add the guide files to the mMoGuide menu.
  246. //
  247. // If there are any files, a dashed line is added first,
  248. // followed by the menu name for each file.  We save
  249. // the FSSpec's for easy retrieval when the user chooses
  250. // the menu item.  If any guide files already exist on
  251. // the menu, they are deleted first and a fresh list
  252. // is prepared.
  253. //
  254. void
  255. TAppMo::AddGuideFiles()
  256. {
  257.     short        newFileCount;
  258.     Str63        menuName;
  259.     FSSpec        fileSpec;
  260.     MenuHandle    hmMoGuide = GetMHandle(mMoGuide);
  261.     if(hmMoGuide)
  262.     {
  263.             // If we already have a file list, delete it.
  264.         if(this->fhFileList)
  265.         {
  266.                 // We do, dispose of it and its menu items.
  267.             DisposeHandle(this->fhFileList);
  268.             while(this->fFileCount--)
  269.             {
  270.                     // Delete the item after the dash line.
  271.                 DelMenuItem(hmMoGuide, iLastMenuItem+2);
  272.             }
  273.                 // Delete dashed line.
  274.             DelMenuItem(hmMoGuide, iLastMenuItem+1);
  275.         }
  276.             // Set the search directory for guide files.
  277.         short    vRefNum = (-*(short*)SFSaveDisk);
  278.         long    dirID = (*(long*)CurDirStore);
  279.         Boolean    wantMixin = false;
  280.         newFileCount = AGFileGetDBCount(vRefNum, dirID,
  281.                                         kAGFileDBTypeAny, wantMixin);
  282.                                     
  283.         if(newFileCount>0)
  284.         {
  285.                 // Create a new list of file FSSpecs.
  286.             this->fhFileList = NewHandle(newFileCount*sizeof(FSSpec));
  287.             if(this->fhFileList)
  288.             {
  289.                     // Add dashed line.
  290.                 AppendMenu(hmMoGuide, "\p-");
  291.                     // Get each file, add to menu and to list.
  292.                 this->fFileCount = newFileCount;
  293.                 for(short i=1; i<=newFileCount; i++)
  294.                 {
  295.                         // Use same vRefNum, dirID, and wantMixin
  296.                         // that was set for the count.
  297.                     if(AGFileGetIndDB(vRefNum, dirID,
  298.                                     kAGFileDBTypeAny, wantMixin,
  299.                                     i, &fileSpec)==noErr)
  300.                     {
  301.                         if(AGFileGetDBMenuName(&fileSpec, menuName)==noErr)
  302.                         {
  303.                                 // We have a guide file,
  304.                                 // get it's menu name and add to menu.
  305.                             AppendMenu(hmMoGuide, menuName);
  306.                             ((FSSpec*)(*this->fhFileList))[i-1] = fileSpec;
  307.                         }
  308.                     }
  309.                 } // for(short…
  310.             } // if(hFileMenu…
  311.         } // if(newFileCount…
  312.     } // if(hmMoGuide…
  313. }
  314.  
  315. // --------------------------------------------------------------------------
  316. // TAppMo::AdjustMenus
  317. //
  318. void
  319. TAppMo::AdjustMenus()            // override
  320. {
  321.             // Inherit first.
  322.     TApp::AdjustMenus();
  323.             // Our menus.
  324.     MenuHandle    hmMoGuide=GetMHandle(mMoGuide);
  325.     MenuHandle    hmWindow=GetMHandle(mWindow);
  326.             // Always enabled
  327.     EnableItem(hmMoGuide, iFiles);
  328.             // Set default case.
  329.     DisableItem(hmMoGuide, iStartGuide);
  330.     DisableItem(hmMoGuide, iQuitGuide);
  331.     DisableItem(hmMoGuide, iOpenDefault);
  332.     DisableItem(hmMoGuide, iClose);
  333.             // If Apple Guide is installed, then we can do lots of things.
  334.     if(gAGuideAvailable)
  335.     {
  336.         AGStatus status = AGGetStatus();
  337.                 // Permit starting it if it's not already started.
  338.         if(status==kAGIsNotRunning)
  339.             EnableItem(hmMoGuide, iStartGuide);
  340.                 // If Apple Guide is sleeping, then we can make it quit.
  341.         else if(status==kAGIsSleeping)
  342.             EnableItem(hmMoGuide, iQuitGuide);
  343.                 // If we have default help available, then we can open it.
  344.         if(AGGetAvailableDBTypes() & kAGDBTypeBitHelp)
  345.             EnableItem(hmMoGuide, iOpenDefault);
  346.                 // If we have a non-nil this->fGuideRefNum,
  347.                 // then a database is open. Permit closing.
  348.                 // this->fGuideRefNum is tracked in TApp::AdjustMenus.
  349.         if(this->fGuideRefNum)
  350.             EnableItem(hmMoGuide, iClose);
  351.                 // Enable any databases that we may have added.
  352.         for(short i=0; i<this->fFileCount; i++)
  353.             EnableItem(hmMoGuide, iLastMenuItem+i+2);
  354.     }
  355.             // Always available.
  356.             // Art-related items.
  357.     EnableItem(hmWindow, iWinArt);
  358.         // Can reset objects if a window is visible.
  359.     if(this->fDocArt)
  360.     {
  361.         EnableItem(hmWindow, iResetArt);
  362.         EnableItem(hmWindow, iShuffleArt);
  363.     }
  364.     else
  365.     {
  366.         DisableItem(hmWindow, iResetArt);
  367.         DisableItem(hmWindow, iShuffleArt);
  368.     }
  369.             // Always available.
  370.     EnableItem(hmWindow, iFeedback);
  371.     EnableItem(hmWindow, iContextTrue);
  372.     CheckItem(hmWindow, iContextTrue, this->fBeepReturn);
  373.     EnableItem(hmWindow, iContextBeep);
  374.     if(this->fFBContext)
  375.         CheckItem(hmWindow, iContextBeep, this->fFBContext->GetBeep());
  376.  
  377. }
  378.  
  379. // ------------------------------------------------------------------------
  380. // TAppMo::CheckFrontWindow
  381. // Check to see if Apple Guide has a window showing.
  382. // Update the miscellaneous feedback with the result.
  383. // We look for the front window of the current database
  384. // which is not necessarily our own. If we wanted just
  385. // our own database, we would pass this->fGuideRefNum
  386. // instead of kAGFrontDatabase.
  387. //
  388. void
  389. TAppMo::CheckFrontWindow()
  390. {
  391.     Str255 string;
  392.     short index=kStrNone;
  393.     if(gAGuideAvailable)
  394.     {
  395.             // If Apple Guide is available, then the choices
  396.             // are nothing (Apple Guide is not running),
  397.             // sleeping, access window, or presentation window.
  398.         if(AGGetStatus()!=kAGIsNotRunning)
  399.         {
  400.             AGWindowKind kind = AGGetFrontWindowKind(kAGFrontDatabase);
  401.                 // Clarity, not conciseness.
  402.             if(kind==kAGNoWindow)
  403.                 index = kStrNoneSleeping;
  404.             else if(kind==kAGAccessWindow)
  405.                 index = kStrAccess;
  406.             else if(kind==kAGPresentationWindow)
  407.                 index = kStrPresentation;
  408.         }
  409.     }
  410.         // Update FBMisc if there is a change.
  411.         // It flashes every time we update it.
  412.         // We force an update every kMaxStatusUpdateCnt cycles.
  413.     this->fLastStatusCnt--;
  414.     if(this->fLastStatusCnt==0 || index!=this->fLastMiscIndex)
  415.     {
  416.         GetIndString(string, kFrontWindowStrId, index);
  417.             // If we are in the idle loop, we must check this->fFBMisc
  418.             // validity in case it hasn't been created yet.
  419.         if(this->fFBMisc)
  420.         {
  421.             this->fFBMisc->SetFlash(string);
  422.             this->fLastMiscIndex = index;
  423.         }
  424.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  425.     }
  426. }
  427.  
  428. // ------------------------------------------------------------------------
  429. // TAppMo::DoAdvance
  430. // If an advance is wanted, do it.
  431. // This is a one-shot event.
  432. // We send a go-back event because we assume that Apple Guide
  433. // is showing an oops panel.
  434. void
  435. TAppMo::DoAdvance()
  436. {
  437.     if(gAGuideAvailable && this->fOkayAdvance)
  438.     {
  439.         AlertIfError(AGGeneral(this->fGuideRefNum, kAGEventReturnBack));
  440.     }
  441.     this->fOkayAdvance = false;
  442. }
  443.  
  444. // ------------------------------------------------------------------------
  445. // TAppMo::DoGoAway
  446. //
  447. // This is the close side of TAppMo::ShowArt, ShowClipboard, ShowFeedback.
  448. //
  449. void
  450. TAppMo::DoGoAway()
  451. {
  452.             // Clear our local document record if
  453.             // it's object is going away.
  454.     if(this->fCurDoc==this->fDocFB)
  455.     {
  456.         this->fDocFB = nil;
  457.         this->fFBContext->SetDoc(nil);
  458.         this->fFBCoach->SetDoc(nil);
  459.         this->fFBEvent->SetDoc(nil);
  460.         this->fFBMisc->SetDoc(nil);
  461.     }
  462.     else if(this->fCurDoc==this->fDocArt)
  463.         this->fDocArt = nil;
  464.             // Inherit go-away action from TApp and TApplication
  465.     TApp::DoGoAway();
  466. }
  467.  
  468. // ------------------------------------------------------------------------
  469. // TAppMo::DoIdle
  470. // Do our idle and deferred events.
  471. //
  472. void
  473. TAppMo::DoIdle()        // override
  474. {
  475.         // Inherit to do TApp Idle.
  476.     TApp::DoIdle();
  477.         // Document actions.
  478.     if(this->fDocArt)
  479.         this->fDocArt->DoIdle();
  480.         // Feedback about context-checking.
  481.     if(this->fFBContext)
  482.         this->fFBContext->DoIdle();
  483.         // Feedback about Coach objects.
  484.     if(this->fFBCoach)
  485.         this->fFBCoach->DoIdle();
  486.         // Feedback about Apple events.
  487.     if(this->fFBEvent)
  488.         this->fFBEvent->DoIdle();
  489.         // Check for Apple Guide front window.
  490.     this->CheckFrontWindow();
  491.         // Miscellaneous feedback.
  492.     if(this->fFBMisc)
  493.         this->fFBMisc->DoIdle();
  494. }
  495.  
  496. // ------------------------------------------------------------------------
  497. // TAppMo::DoMenuCommand
  498. // This is called when an item is chosen from the menu bar (after calling
  499. // MenuSelect or MenuKey). It does the right thing for each command.
  500. //
  501. // TAppMo::AdjustMenus enables the menu items
  502. // depending on whether or not guide is available.
  503. // Consequently, we don't have to check here to see
  504. // if guide is available because, if it isn't, the item
  505. // won't be choosen.
  506. //
  507. void
  508. TAppMo::DoMenuCommand(short menuID, short menuItem)        //  override
  509. {
  510.     FSSpec    fileSpec;
  511.     OSErr    err=noErr;
  512.     switch (menuID)
  513.     {
  514.         case mMoGuide:
  515.             switch(menuItem)
  516.             {
  517.                 case iStartGuide:
  518.                     AlertIfError(AGStart());
  519.                     break;
  520.                 case iQuitGuide:
  521.                     AlertIfError(AGQuit());
  522.                     break;
  523.                 case iOpenDefault:
  524.                     AlertIfError(this->OpenGuideDatabase(kAGDefault));
  525.                     break;
  526.                 case iClose:
  527.                     err = AGClose(&this->fGuideRefNum);
  528.                     if(err==kAGErrDatabaseNotOpen)
  529.                         this->fGuideRefNum = nil;
  530.                     break;
  531.                 case iFiles:
  532.                     this->AddGuideFiles();
  533.                     break;
  534.                 default:
  535.                         // Must be one of the guide file items that we added.
  536.                         // Get its FSSpec from the list and open it.
  537.                     if(this->fhFileList)
  538.                     {
  539.                         fileSpec = ((FSSpec*)(*this->fhFileList))[menuItem-iLastMenuItem-2];
  540.                         AlertIfError(this->OpenGuideDatabase(&fileSpec));
  541.                     }
  542.                     break;
  543.             } // switch
  544.             break;
  545.         case mWindow:
  546.             switch(menuItem)
  547.             {
  548.                 case iWinArt:
  549.                     err = this->SendEventToSelf(kAEIdShowDocArt);
  550.                     break;
  551.                 case iFeedback:
  552.                     err = this->SendEventToSelf(kAEIdShowDocFB);
  553.                     break;
  554.                 case iContextTrue:
  555.                     this->fBeepReturn = !this->fBeepReturn;
  556.                     break;
  557.                 case iContextBeep:
  558.                     if(this->fFBContext)
  559.                         this->fFBContext->SetBeep(!this->fFBContext->GetBeep());
  560.                     break;
  561.                 case iResetArt:
  562.                     err = this->SendEventToSelf(kAEIdReset);
  563.                     break;
  564.                 case iShuffleArt:
  565.                     err = this->SendEventToSelf(kAEIdShuffle);
  566.                     break;
  567.                 default:
  568.                     break;
  569.             } // switch
  570.             break;
  571.         default:
  572.             break;
  573.     } // switch
  574.             // Inherit for the Apple, File, and Edit menus.
  575.     TApp::DoMenuCommand(menuID, menuItem);
  576.     AlertIfError(err);
  577. } // DoMenuCommand
  578.  
  579. // ---------------------------------------------------------------------
  580. // TAppMo::Init
  581. // Do the things that our derived application class requires.
  582. // Basic initialization, before we start.
  583. // We store our application object in the core event handler
  584. // refCon so that the handler can use it.  Avoids making it a global.
  585. // Return noErr if successful.
  586. OSErr
  587. TAppMo::Init()
  588. {
  589.             // Inherit
  590.     OSErr err = TApp::Init();
  591.     if(err==noErr)
  592.     {
  593.             // Override the menubar ID.
  594.         this->fMenuBarID = rMenuBarMo;
  595.             // Clear our collaborators because we'll not want to use
  596.             // them until after we get the core startup event.
  597.                 // Clear handler refNums.
  598.         this->fCoachRefNum = nil;
  599.         this->fContext = nil;
  600.         this->fFBContext = nil;
  601.         this->fFBCoach = nil;
  602.         this->fFBEvent = nil;
  603.         this->fFBMisc = nil;
  604.         this->fDocArt = nil;
  605.         this->fDocFB = nil;
  606.             // Also clear our variables or we may get a bus error
  607.             // at the first DoIdle (before we get the start event.)
  608.         this->fhFileList = nil;
  609.         this->fFileCount = 0;
  610.         this->fBeepReturn = false;
  611.         this->fOkayAdvance = false;
  612.         this->fLastMiscIndex = 0;
  613.             // Remove the core event handler installed in TApp.
  614.         (void) AERemoveEventHandler(kCoreEventClass,
  615.                                 typeWildCard,
  616.                                 kHandlerNotRequired,
  617.                                 kIsNotSysHandler);
  618.             // Replace with a core event handler which
  619.             // uses the same handler, but passes TAppMo
  620.             // in the refCon instead of TApp.  This
  621.             // insures that TAppMo::Start is invoked.
  622.         err = AEInstallEventHandler(kCoreEventClass,
  623.                                 typeWildCard,
  624.                                 (EventHandlerProcPtr) TApp::HandleAECore,
  625.                                 (long)this,
  626.                                 kIsNotSysHandler);
  627.             // Install our custom event handler.
  628.             // We put the TAppMo object in the refCon.
  629.         err = AEInstallEventHandler(kAEClassCustom,
  630.                                 typeWildCard,
  631.                                 (EventHandlerProcPtr) TAppMo::HandleAECustom,
  632.                                 (long)this,
  633.                                 kIsNotSysHandler);
  634.             // Initialize our status update cycle counter.
  635.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  636.             // Starting is done in TApp::HandleAECore.
  637.             // We wait until that happens before proceeding.
  638.     }
  639.     return err;
  640. }
  641.  
  642. // ------------------------------------------------------------------------
  643. // TAppMo::Quit
  644. // We're quitting, so delete everything.
  645. // All of this probably goes away in the application heap anyway,
  646. // we're just being compulsively tidy.
  647. //
  648. void
  649. TAppMo::Quit()
  650. {
  651.     if(gAGuideAvailable)
  652.     {
  653.                 // Remove Coach and context handlers.
  654.         AlertIfError(AGRemoveCoachHandler(&this->fCoachRefNum));
  655.     }
  656.             // Delete our context object.
  657.     if(this->fContext)
  658.     {
  659.         AlertIfError(this->fContext->Remove());
  660.         delete this->fContext;
  661.     }
  662.             // Delete our flasher objects.
  663.     if(this->fFBContext)
  664.         delete this->fFBContext;
  665.     if(this->fFBCoach)
  666.         delete this->fFBCoach;
  667.     if(this->fFBEvent)
  668.         delete this->fFBEvent;
  669.     if(this->fFBMisc)
  670.         delete this->fFBMisc;
  671.             // Remove list of guide files.
  672.     if(this->fhFileList)
  673.         DisposeHandle(this->fhFileList);
  674.             // Remove custom event handler.
  675.     (void) AERemoveEventHandler(kAEClassCustom,
  676.                                 typeWildCard,
  677.                                 kHandlerNotRequired,
  678.                                 kIsNotSysHandler);
  679.             // Inherit.
  680.     TApp::Quit();
  681. }
  682.  
  683. // ------------------------------------------------------------------------
  684. // TAppMo::ShowArt
  685. // Opens the art window.
  686. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  687. //
  688. // The art is specified in a resource.
  689. // The art resource id matches the id of its window.
  690. // To add another window, specify a new resource id,
  691. // define the new art resource, and instantiate a new TDocArt.
  692. //
  693. void
  694. TAppMo::ShowArt()
  695. {
  696.     if(this->fDocArt==nil)
  697.     {
  698.             // Art window is not present.
  699.             // Create a document and window for the art.
  700.         this->fDocArt = new TDocArt(kArt1WindResID);
  701.         if(this->fDocArt)
  702.         {
  703.                 // Add new document to document list.
  704.             this->fDocList->AddDoc((TDocument*) this->fDocArt);
  705.                 // Update current document and window pointer.
  706.             this->fCurDoc = this->fDocArt;
  707.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  708.             SetPort(this->fWhichWindow);
  709.                 // Initialize art document window.
  710.             AlertIfError(this->fDocArt->Init(kArt1WindResID));
  711.                 // Set the application object as a collaborator.
  712.             this->fDocArt->SetApp(this);
  713.         }
  714.     }
  715.         // Better show it, or at least bring it to the front.
  716.     this->fDocArt->Show();
  717.             // Invalidate window so that it will be updated and drawn.
  718.     this->fDocArt->Invalidate();
  719. }
  720.  
  721. // ------------------------------------------------------------------------
  722. // TAppMo::ShowFeedback
  723. // Opens the feedback window.
  724. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  725. //
  726. void
  727. TAppMo::ShowFeedback()
  728. {
  729.     if(this->fDocFB==nil)
  730.     {
  731.             // Feedback window is not present.
  732.             // Create a document and window for the feedback.
  733.         this->fDocFB = new TDocFB(kFeedbackWinResID);
  734.         if(this->fDocFB)
  735.         {
  736.                 // Add new document to document list.
  737.             this->fDocList->AddDoc((TDocument*) this->fDocFB);
  738.                 // Update current document and window pointer.
  739.             this->fCurDoc = this->fDocFB;
  740.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  741.             SetPort(this->fWhichWindow);
  742.                     // Initialize feedback document window.
  743.             AlertIfError(this->fDocFB->Init());
  744.                 // Set the application object as a collaborator.
  745.             this->fDocFB->SetApp(this);
  746.                 // Set document window collaborator for feedback.
  747.             this->fFBContext->SetDoc(this->fDocFB);
  748.             this->fFBCoach->SetDoc(this->fDocFB);
  749.             this->fFBEvent->SetDoc(this->fDocFB);
  750.             this->fFBMisc->SetDoc(this->fDocFB);
  751.         }
  752.     }
  753.         // Better show it, or at least bring it to the front.
  754.     this->fDocFB->Show();
  755.             // Invalidate window so that it will be updated and drawn.
  756.     this->fDocFB->Invalidate();
  757. }
  758.  
  759. // ---------------------------------------------------------------------
  760. // TAppMo::Start
  761. // Startup the application.
  762. // The Init is done in main.  The Start comes with the oapp or odoc event.
  763. // Return noErr if successful.
  764. OSErr
  765. TAppMo::Start()
  766. {
  767.             // Inherit.
  768.     OSErr err = TApp::Start();
  769.     {
  770.                 // Reply to Coach mark queries.
  771.                 // Pass along the TApp object.
  772.                 // Setup our callbacks for context-check queries.
  773.                 // Pass along the TApp object in the refCon.
  774.         if(gAGuideAvailable)
  775.         {
  776.             err = AGInstallCoachHandler((CoachReplyProcPtr)TAppMo::ReplyToCoach,
  777.                                         (long)this, &this->fCoachRefNum);
  778.             if(err!=noErr)
  779.                 return err;
  780.             this->fContext = new TContext;
  781.             if(this->fContext)
  782.                 err = this->fContext->Install((long)this);
  783.             else
  784.                 err = kErrNoContextObject;
  785.             if(err!=noErr)
  786.                 return err;
  787.         }
  788.                 // List of guide files.
  789.         this->fhFileList = nil;
  790.         this->fFileCount = 0;
  791.                 // Don't want a panel advance yet.
  792.         this->fOkayAdvance = false;
  793.                 // Return a true or false to the beep context check query?
  794.         this->fBeepReturn = false;
  795.                 // Feedback (the Flasher).
  796.         this->fFBContext = new TFeedback;
  797.         if(this->fFBContext)
  798.             this->fFBContext->Init(this, dataCC);
  799.         this->fFBCoach = new TFeedback;
  800.         if(this->fFBCoach)
  801.             this->fFBCoach->Init(this, dataCH);
  802.         this->fFBEvent = new TFeedback;
  803.         if(this->fFBEvent)
  804.             this->fFBEvent->Init(this, dataEV);
  805.         this->fFBMisc = new TFeedback;
  806.         if(this->fFBMisc)
  807.         {
  808.             this->fFBMisc->Init(this, dataTP);
  809.                 // Set an infinite interval for FBMisc.
  810.             this->fFBMisc->SetInterval(0);
  811.         }
  812.                 // Show the feedback window/document.
  813.         err = this->SendEventToSelf(kAEIdShowDocFB);
  814.     }
  815.     return err;
  816. }
  817.  
  818. // =========================================================================
  819. // TContext
  820. // ------------------------------------------------------------------------
  821. TContext::TContext()
  822. {
  823.     this->fHandlerCnt = 0;
  824. }
  825.  
  826. // ------------------------------------------------------------------------
  827. TContext::~TContext()
  828. {
  829.         // If we still have handlers installed, remove them.
  830.     if(this->fHandlerCnt>0)
  831.         (void) this->Remove();
  832. }
  833.  
  834. // ------------------------------------------------------------------------
  835. // TContext::Install
  836. // Install context-check handlers.
  837. // The appObj is the application object
  838. // used in the reply to access the application.
  839. // If any error occurs, return it.
  840. //
  841. OSErr
  842. TContext::Install(long appObj)
  843. {
  844.     OSErr result=noErr;
  845.     OSErr eachResult;
  846.         // Define and install context-check handlers.
  847.         // If we still have handlers installed, remove them.
  848.     if(this->fHandlerCnt>0)
  849.         result = this->Remove();
  850.     this->fHandlerCnt = kContextHandlerCnt;
  851.     this->fContext[0].eventID = kAEIdContextCollision;
  852.     this->fContext[1].eventID = kAEIdContextBeep;
  853.     this->fContext[2].eventID = kAEIdContextWindow;
  854.     for(short i=0; i<this->fHandlerCnt; i++)
  855.     {
  856.             // Install handler.
  857.             // If an error, the refNum will be set to nil by Apple Guide.
  858.         eachResult = AGInstallContextHandler(
  859.                                         (ContextReplyProcPtr)TContext::ReplyToContext,
  860.                                         this->fContext[i].eventID,
  861.                                         appObj,
  862.                                         &this->fContext[i].refNum);
  863.             // Track only errors in result.
  864.         result = (eachResult!=noErr)?eachResult:result;
  865.     }
  866.     return result;
  867. }
  868.  
  869. // ------------------------------------------------------------------------
  870. // TContext::Remove
  871. // Remove context-check handlers.
  872. // If any error occurs, return it.
  873. //
  874. OSErr
  875. TContext::Remove()
  876. {
  877.     OSErr result=noErr;
  878.     OSErr eachResult;
  879.     for(short i=0; i<this->fHandlerCnt; i++)
  880.     {
  881.         eachResult = AGRemoveContextHandler(&this->fContext[i].refNum);
  882.             // Track only errors in result.
  883.         result = (eachResult!=noErr)?eachResult:result;
  884.     }
  885.     this->fHandlerCnt = 0;
  886.     return result;
  887. }
  888.  
  889. // =========================================================================
  890. // TTypeStr
  891. // ------------------------------------------------------------------------
  892. TTypeStr::TTypeStr(OSType theType)
  893. {
  894.     TypeToChar toConvert;
  895.     toConvert.type = theType;
  896.             // Convert stored OSType to pstring.
  897.             // Replace zeros with spaces.
  898.     this->fString[0] = 4;
  899.     for(short i=0; i<4; i++)
  900.         this->fString[i+1] = (toConvert.ch[i])?toConvert.ch[i]:' ';
  901. }
  902.  
  903. // ------------------------------------------------------------------------
  904. // TTypeStr::String
  905. StringPtr
  906. TTypeStr::String()
  907. {
  908.     return &this->fString[0];
  909. }
  910.  
  911. // =========================================================================
  912. // TFeedback
  913. // ------------------------------------------------------------------------
  914. TFeedback::TFeedback()
  915. {
  916.     this->fShowTime = 0;
  917.     this->fInterval = kIntervalShow;
  918.     this->fCurrent = 0;
  919.     this->fPrevious = 0;
  920.     this->fShow = false;
  921.     this->fBeep = false;
  922.     this->fOurApp = nil;
  923.     this->fDocFB = nil;
  924. }
  925.  
  926. // ------------------------------------------------------------------------
  927. TFeedback::~TFeedback()
  928. {
  929. }
  930.  
  931. // ------------------------------------------------------------------------
  932. // TFeedback::DoIdle
  933. // Do any action required during the idle processing.
  934. //
  935. void
  936. TFeedback::DoIdle()
  937. {
  938.     this->Show();
  939. }
  940.  
  941. // ------------------------------------------------------------------------
  942. // TFeedback::Draw
  943. void
  944. TFeedback::Draw(OSType toDraw, short mode)
  945. {
  946.     TTypeStr flashChar = toDraw;
  947.             // Draw it in the feedback window (if present).
  948.     if(this->fDocFB)
  949.         this->fDocFB->DrawData(flashChar.String(), mode, this->fWhichData);
  950. }
  951.  
  952. // ------------------------------------------------------------------------
  953. // TFeedback::GetBeep
  954. // Return the want-beep flag.
  955. Boolean
  956. TFeedback::GetBeep()
  957. {
  958.     return this->fBeep;
  959. }
  960.  
  961. // ------------------------------------------------------------------------
  962. // TFeedback::Init
  963. // Set the application collaborator (and any others you might want to add.)
  964. void
  965. TFeedback::Init(TAppMo* theApp, short whichData)
  966. {
  967.         // The collaborator
  968.     this->fOurApp = theApp;
  969.         // The data
  970.     this->fWhichData = whichData;
  971. }
  972.  
  973. // ------------------------------------------------------------------------
  974. // TFeedback::SetBeep
  975. // Set the want-beep flag.
  976. void
  977. TFeedback::SetBeep(Boolean wantBeep)
  978. {
  979.     this->fBeep = wantBeep;
  980. }
  981.  
  982. // ------------------------------------------------------------------------
  983. // TFeedback::SetFlash
  984. void
  985. TFeedback::SetFlash(OSType theFlash)
  986. {
  987.     this->fCurrent = theFlash;
  988.     this->fShow = true;
  989. }
  990.  
  991. // ------------------------------------------------------------------------
  992. // TFeedback::SetFlash
  993. // Accept a char ptr, convert its first four characters to an OSType.
  994. void
  995. TFeedback::SetFlash(Ptr theFlash)
  996. {
  997.     CharToType    flashChar;
  998.     flashChar.type = '    ';
  999.     for(short i=0; i<4; i++)
  1000.         if(theFlash[i]!=0)
  1001.             flashChar.ch[i] = theFlash[i];
  1002.     this->fCurrent = flashChar.type;
  1003.     this->fShow = true;
  1004. }
  1005.  
  1006. // ------------------------------------------------------------------------
  1007. // TFeedback::SetFlash
  1008. // Accept a pascal string, convert its first four characters to an OSType.
  1009. void
  1010. TFeedback::SetFlash(Str255 theFlash)
  1011. {
  1012.     short len = (theFlash[0]>4)?4:theFlash[0];
  1013.     CharToType    flashChar;
  1014.     flashChar.type = '    ';
  1015.     for(short i=0; i<len; i++)
  1016.         flashChar.ch[i] = theFlash[i+1];
  1017.     this->fCurrent = flashChar.type;
  1018.     this->fShow = true;
  1019. }
  1020.  
  1021. // ------------------------------------------------------------------------
  1022. // TFeedback::Show
  1023. // Show anything new.
  1024. // When interval is up, erase.
  1025. //
  1026. void
  1027. TFeedback::Show()
  1028. {
  1029.     short drawThisCycle=true;
  1030.     if(this->fShow)
  1031.     {
  1032.         if(this->fPrevious)
  1033.         {
  1034.                 // Something is up there, erase it.
  1035.             this->Draw(this->fPrevious, kErase);
  1036.                 // Is this a repeat of the same item?
  1037.             if(this->fPrevious==this->fCurrent)
  1038.             {
  1039.                     // Same item, don't draw this cycle.
  1040.                     // Delay one cycle to make it "flash".
  1041.                     // Unless the sysbeep is on, that will
  1042.                     // give enough delay to make it flash.
  1043.                 drawThisCycle = this->fBeep;
  1044.             }
  1045.                 // Clear our previous item, it was erased.
  1046.             this->fPrevious = 0;
  1047.         }
  1048.             // Auditory feedback and delay between erase and draw.
  1049.         if(this->fBeep)
  1050.         {
  1051.             SysBeep(1);
  1052.         }
  1053.             // Show.
  1054.         if(drawThisCycle)
  1055.         {
  1056.             this->fShow = false;
  1057.             this->fShowTime = TickCount();
  1058.             this->Draw(this->fCurrent, kPaint);
  1059.             this->fPrevious = this->fCurrent;
  1060.         }
  1061.     }
  1062.     else
  1063.     {
  1064.             // Nothing new to show.
  1065.             // Only erase previous if we have a positive interval.
  1066.         if(this->fPrevious && this->fInterval)
  1067.         {
  1068.             if(TickCount()>this->fShowTime+this->fInterval)
  1069.             {
  1070.                 // Counter timed out and no new stuff.
  1071.                 // Erase old stuff.
  1072.                     // Something is up there, erase it.
  1073.                 this->Draw(this->fPrevious, kErase);
  1074.                 this->fPrevious = 0;
  1075.             }
  1076.         }
  1077.     }
  1078. }
  1079.     
  1080.